package drds.plus.sql_process.optimizer.cost_esitimater;

import drds.plus.sql_process.abstract_syntax_tree.configuration.ColumnMetaData;
import drds.plus.sql_process.abstract_syntax_tree.configuration.IndexMapping;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.column.Column;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.BooleanFilter;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.Filter;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.Operation;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.OrsFilter;
import drds.plus.sql_process.abstract_syntax_tree.node.query.*;
import drds.plus.sql_process.optimizer.cost_esitimater.statistics.TableIndexStatisticsItem;
import drds.tools.$;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CostEsitimaterFactory {

    public static JoinNodeCostEstimater joinNodeCostEstimater = new JoinNodeCostEstimater();
    public static MergeNodeCostEstimater mergeNodeCostEstimater = new MergeNodeCostEstimater();
    public static QueryNodeCostEstimater queryNodeCostEstimater = new QueryNodeCostEstimater();

    public static Cost zeroCost = new Cost();

    public static Cost estimate(Query query) {

        // return zeroCost;
        if (query instanceof Join) {
            return joinNodeCostEstimater.estimate(query);
        } else if (query instanceof MergeQuery) {
            return mergeNodeCostEstimater.estimate(query);
        } else if (query instanceof $Query$ || query instanceof TableQuery) {
            return queryNodeCostEstimater.estimate(query);
        } else {
            throw new UnsupportedOperationException();
        }
    }

    /**
     * 根据索引和查询的filter条件，估算记录数
     *
     * @param tableRowCount            表记录数
     * @param filterList               查询条件
     * @param indexMapping             选择的索引
     * @param tableIndexStatisticsItem 索引的统计信息，如果为null则按照经验值预算
     * @return
     */
    public static long estimateRowCount(long tableRowCount, List<Filter> filterList, IndexMapping indexMapping, TableIndexStatisticsItem tableIndexStatisticsItem) {
        if (!$.isNotNullAndHasElement(filterList)) {
            return tableRowCount;
        }
        Map<String, Double> columnToKeyColumnCountAndDistinctKeyCountSelectivityMap = new HashMap();
        if (indexMapping != null && tableIndexStatisticsItem != null) {
            Double columnCountAndDistinctKeyCountSelectivity = (indexMapping.getKeyColumnMetaDataList().size()) * (1.0 / tableIndexStatisticsItem.getDistinctKeyCount());
            for (ColumnMetaData columnMetaData : indexMapping.getKeyColumnMetaDataList()) {
                columnToKeyColumnCountAndDistinctKeyCountSelectivityMap.put(columnMetaData.getColumnName(), columnCountAndDistinctKeyCountSelectivity);
            }
        }

        long count = tableRowCount;
        // 每出现一个运算符，都把现在的行数乘上一个系数
        for (Filter filter : filterList) {
            if (filter == null) {
                break;
            }
            if (filter instanceof OrsFilter) {
                continue;
            }
            BooleanFilter booleanFilter = (BooleanFilter) filter;
            Double selectivity = null;
            if (booleanFilter.getColumn() instanceof Column) {
                String columnName = ((Column) booleanFilter.getColumn()).getColumnName();
                if (columnToKeyColumnCountAndDistinctKeyCountSelectivityMap.containsKey(columnName)) {
                    selectivity = columnToKeyColumnCountAndDistinctKeyCountSelectivityMap.get(columnName);
                }
            }
            if (selectivity == null) {
                selectivity = selectivity(booleanFilter.getOperation());
            }
            count *= selectivity;
        }
        return count;
    }

    /**
     * 参考derby数据库实现
     */
    private static double selectivity(Operation operator) {
        if (operator == Operation.equal) {
            return 0.1;
        }
        if (operator == Operation.greater_than || operator == Operation.greater_than_or_equal) {
            return 0.33;
        }

        if (operator == Operation.less_than || operator == Operation.less_than_or_equal) {
            return 0.33;
        }

        if (operator == Operation.not_equal) {
            return 0.9;
        }

        if (operator == Operation.is_null) {
            return 0.1;
        }

        if (operator == Operation.is_not_null) {
            return 0.9;
        }

        if (operator == Operation.like) {
            return 0.9;
        }
        if (operator == Operation.in) {
            return 0.2;
        }

        return 0.5;
    }
}
